﻿using PDMan.staticSource;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Zero.Common.Data;

namespace PDMan
{
    public class Builder
    {
        /// <summary>
        /// 根据数据库设计文件生成实体代码
        /// </summary>
        /// <param name="file">数据库设计文件</param>
        /// <param name="path">代码保存目录</param>
        /// <returns>操作结果</returns>
        static public Result ToCSharp(string file, string path)
        {
            var result = new Result();

            #region 尝试加载文件

            if (!File.Exists(file))
            {
                result.Details = $"文件{file}不存在！";
                return result;
            }
            PDMan root = null;
            try
            {
                string json = File.ReadAllText(file, Encoding.UTF8);
                json = json.Replace("C#", "CSharp");
                var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<PDMan>(json);
                if (obj != null)
                {
                    root = obj;
                }

            }
            catch (Exception ex)
            {
                result.Details = $"导入PDMan文件{file}失败！详见异常。";
                result.SourceResult = new Result(false, ex.Message);
                return result;
            }
            if (root == null)
            {
                result.Details = $"无法识别PDMan文件{file}！";
                return result;
            }

            #endregion


            foreach (var module in root.modules)
            {
                var folder = Path.Combine(path, module.name);
                if (!Directory.Exists(folder))
                {
                    Directory.CreateDirectory(folder);
                }

                var dbContext = Path.Combine(folder, $"MyDbContext.cs");
                var strDbContext = new StringBuilder();
                //using和命名空间
                strDbContext.AppendLine("using System;");
                strDbContext.AppendLine("using System.Collections.Generic;");
                strDbContext.AppendLine("using System.Data.Entity;");
                strDbContext.AppendLine();
                strDbContext.AppendLine("namespace DbEntity.Data");
                strDbContext.AppendLine("{");
                strDbContext.AppendLine($"    public class MyDbContext : DbContext");
                strDbContext.AppendLine("    {");
                foreach (var entity in module.entities)
                {
                    var code = ToCSharp(root, entity, CacheModel.NameSpace);
                    var fileName = Path.Combine(folder, $"{entity.title}.cs");
                    var fileModelName = Path.Combine(folder, $"{entity.title}_Model.cs");
                    //生成数据库实体 
                    File.WriteAllText(fileName, code, Encoding.UTF8);

                    var modelText = code.Replace($"{entity.title}", $"{entity.title}_Model");
                    if (!string.IsNullOrWhiteSpace(CacheModel.Model_NameSpace))
                    {
                        modelText = modelText.Replace($"{CacheModel.NameSpace}", $"{CacheModel.Model_NameSpace}");
                    }
                    //生成一个相同的模型
                    File.WriteAllText(fileModelName, modelText, Encoding.UTF8);
                    #region dbContext 
                    strDbContext.AppendLine($"        public DbSet<{entity.title}> {entity.title} " + "{ get; set; }");
                    #endregion
                    if (CacheModel.Sysnonys.Count > 0)
                        File.WriteAllText(folder + "\\SYNONYM.Sql", string.Join("", CacheModel.Sysnonys));
                }
                strDbContext.AppendLine("    }");
                strDbContext.AppendLine("}");
                File.WriteAllText(dbContext, strDbContext.ToString(), Encoding.UTF8);
            }
            result.Success = true;

            return result;
        }

        #region 基础方法

        //转为C#类型
        static private string ToCSharpType(List<DatatypeItem> datatype, string code, List<Item> items)
        {
            var type = datatype.FirstOrDefault(item => item.code == code);
            var citem = items.First(d => d.SQL.ToLower() == type.apply.SQLServer.type.ToLower());
            if (citem == null)
            {
                System.Windows.Forms.MessageBox.Show($"尝试转换 [{type.apply.SQLServer.type}] 类型失败，请检查模板.");
            }
            var ctype = citem.CSharp;
            return ctype;
        }

        //获取实体中文名
        static private string ToEntityName(PDMan root, string title)
        {
            var entity = root.modules.SelectMany(item => item.entities).FirstOrDefault(item => item.title == title);
            return entity?.chnname;
        }

        #endregion

        //核心方法（生成某实体代码）
        static private string ToCSharp(PDMan root, EntitiesItem entity, string _namespace)
        {
            StringBuilder stringBuilder = new StringBuilder();
            var path = Path.GetFullPath("SQLToCSharp.xml");
            var xml = System.IO.File.ReadAllText(path);
            var items = XmlSerializeHelper.DESerializer<List<Item>>(xml);
            //using和命名空间
            stringBuilder.AppendLine("using System;");
            stringBuilder.AppendLine("using System.Collections.Generic;");
            stringBuilder.AppendLine("using System.ComponentModel.DataAnnotations;");
            stringBuilder.AppendLine("using System.ComponentModel.DataAnnotations.Schema;");
            stringBuilder.AppendLine("using System.Runtime.Serialization;");
            stringBuilder.AppendLine();
            stringBuilder.AppendLine(string.IsNullOrWhiteSpace(_namespace) ? "namespace Entity.Models" : ("namespace  " + _namespace));
            stringBuilder.AppendLine("{");

            //实体对象
            stringBuilder.AppendLine("    /// <summary>");
            stringBuilder.AppendLine($"    /// {entity.chnname}");
            stringBuilder.AppendLine("    /// </summary>");
            stringBuilder.AppendLine("    [Serializable]");
            stringBuilder.AppendLine("    [DataContract]");
            var title = string.Empty;
            if (string.IsNullOrWhiteSpace(CacheModel.ModelQz))
            {
                title = entity.title;
            }
            else
            {
                title = (CacheModel.ModelQz + "_" + entity.title);
                //创建数据库同义词 
                CacheModel.Sysnonys.Add($"CREATE SYNONYM[dbo].[{title}] FOR [{CacheModel.ModelQz}].[dbo].[{entity.title}];\r\n");
            }
            stringBuilder.AppendLine($"    [Table(\"{ title }\")]");
            stringBuilder.AppendLine($"    public partial class {entity.title}");
            stringBuilder.AppendLine("    {");
            stringBuilder.AppendLine("        /// <summary>");
            stringBuilder.AppendLine("        /// 构造函数");
            stringBuilder.AppendLine("        /// </summary>");
            stringBuilder.AppendLine($"        public {entity.title}()");
            stringBuilder.AppendLine("        {");
            stringBuilder.AppendLine();
            stringBuilder.AppendLine("        }");
            stringBuilder.AppendLine();

            //字段
            stringBuilder.AppendLine("        #region 字段");
            stringBuilder.AppendLine();
            for (int i = 0; i < entity.fields.Count; i++)
            {
                var field = entity.fields[i];
                stringBuilder.AppendLine("        /// <summary>");
                stringBuilder.AppendLine($"        /// {field.chnname}");
                stringBuilder.AppendLine("        /// </summary>");
                if (!string.IsNullOrEmpty(field.remark))
                {
                    stringBuilder.AppendLine("        /// <remarks>");
                    stringBuilder.AppendLine($"        /// {field.remark}");
                    stringBuilder.AppendLine("        /// </remarks>");
                }
                if (field.pk)
                {
                    stringBuilder.AppendLine($"        [Key]");
                }
                stringBuilder.AppendLine($"        [DataMember(Order={i + 1})]");
                stringBuilder.AppendLine($"        public {ToCSharpType(root.dataTypeDomains.datatype, field.type, items)} {field.name} " + "{ get; set; }");
                stringBuilder.AppendLine();
            }
            stringBuilder.AppendLine();
            stringBuilder.AppendLine("        #endregion");
            stringBuilder.AppendLine();

            var pkList = root.modules.SelectMany(item => item.associations.Where(a => a.from.entity == entity.title));
            var fkList = root.modules.SelectMany(item => item.associations.Where(a => a.to.entity == entity.title));
            if (pkList.Count() > 0)
            {
                stringBuilder.AppendLine("        #region 依赖主键实体");
                stringBuilder.AppendLine();
                foreach (var pk in pkList)
                {
                    stringBuilder.AppendLine("        /// <summary>");
                    stringBuilder.AppendLine($"        /// {ToEntityName(root, pk.to.entity)}");
                    stringBuilder.AppendLine("        /// </summary>");
                    stringBuilder.AppendLine($"        [ForeignKey(\"{pk.from.field}\")]");
                    stringBuilder.AppendLine($"        public virtual {pk.to.entity} {pk.to.entity} " + "{ get; set; }");
                    stringBuilder.AppendLine();
                }
                stringBuilder.AppendLine();
                stringBuilder.AppendLine("        #endregion");
                stringBuilder.AppendLine();
            }

            if (fkList.Count() > 0)
            {
                stringBuilder.AppendLine("        #region 包含外键实体集合");
                stringBuilder.AppendLine();
                foreach (var fk in fkList)
                {
                    stringBuilder.AppendLine("        /// <summary>");
                    stringBuilder.AppendLine($"        /// {ToEntityName(root, fk.from.entity)}");
                    stringBuilder.AppendLine("        /// </summary>");
                    stringBuilder.AppendLine($"        public virtual List<{fk.from.entity}> {fk.from.entity}_List " + "{ get; set; }");
                    stringBuilder.AppendLine();
                }
                stringBuilder.AppendLine();
                stringBuilder.AppendLine("        #endregion");
                stringBuilder.AppendLine();
            }

            stringBuilder.AppendLine("    }");
            /*
            //实体对象配置
            stringBuilder.AppendLine("    /// <summary>");
            stringBuilder.AppendLine($"    /// {entity.chnname}的配置文件");
            stringBuilder.AppendLine("    /// </summary>");
            stringBuilder.AppendLine($"    public partial class {entity.title}Configuration : EntityTypeConfiguration<{entity.title}>");
            stringBuilder.AppendLine("    {");
            stringBuilder.AppendLine($"        public {entity.title}Configuration()");
            stringBuilder.AppendLine("        {");
            stringBuilder.AppendLine("        }");
            
            stringBuilder.AppendLine("        /// <summary>");
            stringBuilder.AppendLine("        /// 配置方法");
            stringBuilder.AppendLine("        /// </summary>");
            stringBuilder.AppendLine($"        public void Configure(EntityTypeBuilder<{entity.title}> builder)");
            stringBuilder.AppendLine("        {");
            if (pkList.Count() > 0)
            {
                stringBuilder.AppendLine("            //依赖主键");
                foreach (var pk in pkList)
                {
                    stringBuilder.AppendLine($"            builder.HasOne(x => x.{pk.entity}).WithMany(op => op.{entity.title}List).IsRequired(true).HasForeignKey(@\"{pk.field}\");");
                }
            }
            stringBuilder.AppendLine();
            if (pkList.Count() > 0)
            {
                stringBuilder.AppendLine("            //包含外键");
                foreach (var fk in fkList)
                {
                    stringBuilder.AppendLine($"            builder.HasMany(x => x.{fk.entity}List).WithOne(op => op.{entity.title}).IsRequired(true).HasForeignKey(@\"{fk.field}\");");
                }
            }
            stringBuilder.AppendLine("        }");
            
            stringBuilder.AppendLine();
            stringBuilder.AppendLine("    }");
            stringBuilder.AppendLine();
            */
            stringBuilder.AppendLine("}");
            return stringBuilder.ToString();
        }
    }
}
